// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import (
	"internal/abi"
	"internal/goarch"
	"runtime/internal/math"
	"runtime/internal/sys"
	"unsafe"
)

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

// notInHeapSlice是由go:notinheap内存支持的片。
type notInHeapSlice struct {
	array *notInHeap
	len   int
	cap   int
}

func panicmakeslicelen() {
	panic(errorString("makeslice: len out of range"))
}

func panicmakeslicecap() {
	panic(errorString("makeslice: cap out of range"))
}

// makeslicecopy分配一个“et”类型的“tolen”元素片，然后将“et”类型的“fromlen”元素从“from”复制到新的分配中。
func makeslicecopy(et *_type, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer {
	var tomem, copymem uintptr
	if uintptr(tolen) > uintptr(fromlen) {
		var overflow bool
		tomem, overflow = math.MulUintptr(et.size, uintptr(tolen))
		if overflow || tomem > maxAlloc || tolen < 0 {
			panicmakeslicelen()
		}
		copymem = et.size * uintptr(fromlen)
	} else {
		// fromlen是一个已知的良好长度，等于或大于tolen，
		// 从而使tolen也成为一个良好的切片长度，因为from和to切片的
		// 元素宽度相同。
		tomem = et.size * uintptr(tolen)
		copymem = tomem
	}

	var to unsafe.Pointer
	if et.ptrdata == 0 {
		to = mallocgc(tomem, nil, false)
		if copymem < tomem {
			memclrNoHeapPointers(add(to, copymem), tomem-copymem)
		}
	} else {
		// 注意：不能使用rawmem（这可以避免内存归零），因为这样GC就可以扫描未初始化的内存。
		to = mallocgc(tomem, et, true)
		if copymem > 0 && writeBarrier.enabled {
			// 只在旧版中隐藏指针。数组，因为我们知道
			// 的目标片只包含零指针，因为它在alloc期间被清除。
			bulkBarrierPreWriteSrcOnly(uintptr(to), uintptr(from), copymem)
		}
	}

	if raceenabled {
		callerpc := getcallerpc()
		pc := abi.FuncPCABIInternal(makeslicecopy)
		racereadrangepc(from, copymem, callerpc, pc)
	}
	if msanenabled {
		msanread(from, copymem)
	}
	if asanenabled {
		asanread(from, copymem)
	}

	memmove(to, from, copymem)

	return to
}

func makeslice(et *_type, len, cap int) unsafe.Pointer {
	mem, overflow := math.MulUintptr(et.size, uintptr(cap))
	if overflow || mem > maxAlloc || len < 0 || len > cap {
		// 注意：如果有人犯了（[]T，bignumber）错误，则产生“len out range”错误，而不是
		// “cap out range”错误。
		// “上限超出范围”也是正确的，但由于上限只是被
		// 含蓄地提供，表示len更清晰。
		// 见戈朗。org/issue/4085。
		mem, overflow := math.MulUintptr(et.size, uintptr(len))
		if overflow || mem > maxAlloc || len < 0 {
			panicmakeslicelen()
		}
		panicmakeslicecap()
	}

	return mallocgc(mem, et, true)
}

func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {
	len := int(len64)
	if int64(len) != len64 {
		panicmakeslicelen()
	}

	cap := int(cap64)
	if int64(cap) != cap64 {
		panicmakeslicecap()
	}

	return makeslice(et, len, cap)
}

func unsafeslice(et *_type, ptr unsafe.Pointer, len int) {
	if len < 0 {
		panicunsafeslicelen()
	}

	mem, overflow := math.MulUintptr(et.size, uintptr(len))
	if overflow || mem > -uintptr(ptr) {
		if ptr == nil {
			panic(errorString("unsafe.Slice: ptr is nil and len is not zero"))
		}
		panicunsafeslicelen()
	}
}

func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) {
	len := int(len64)
	if int64(len) != len64 {
		panicunsafeslicelen()
	}
	unsafeslice(et, ptr, len)
}

func unsafeslicecheckptr(et *_type, ptr unsafe.Pointer, len64 int64) {
	unsafeslice64(et, ptr, len64)

	// 检查底层数组是否跨越多个堆对象。
	// unsafeslice64已检查溢出。
	if checkptrStraddles(ptr, uintptr(len64)*et.size) {
		throw("checkptr: unsafe.Slice result straddles multiple allocations")
	}
}

func panicunsafeslicelen() {
	panic(errorString("unsafe.Slice: len out of range"))
}

// growtslice在追加期间处理切片增长。
// 它被传递切片元素类型、旧切片和所需的新最小容量
// 并返回一个至少具有该容量的新切片，旧数据
// 复制到其中。
// 新片的长度设置为旧片的长度，
// 不设置为新请求的容量。
// 这是为了codegen的方便。旧切片的长度立即用于
// 计算在追加过程中写入新值的位置。
// 待办事项：当旧的后端消失时，重新考虑这个决定。
// SSA后端可能更喜欢新的长度，或者只返回ptr/cap并节省堆栈空间。
func growslice(et *_type, old slice, cap int) slice {
	if raceenabled {
		callerpc := getcallerpc()
		racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, abi.FuncPCABIInternal(growslice))
	}
	if msanenabled {
		msanread(old.array, uintptr(old.len*int(et.size)))
	}
	if asanenabled {
		asanread(old.array, uintptr(old.len*int(et.size)))
	}

	if cap < old.cap {
		panic(errorString("growslice: cap out of range"))
	}

	if et.size == 0 {
		// append不应创建指针为nil但len为非零的切片。
		// 我们假设append不需要保存旧的。在本例中为数组。
		return slice{unsafe.Pointer(&zerobase), old.len, cap}
	}

	newcap := old.cap
	doublecap := newcap + newcap
	if cap > doublecap {
		newcap = cap
	} else {
		const threshold = 256
		if old.cap < threshold {
			newcap = doublecap
		} else {
			// 检查0<newcap以检测溢出
			// 并防止无限循环。
			for 0 < newcap && newcap < cap {
				// 从小切片的2倍增长到大切片的1.25倍增长。这个公式
				// 给出了两者之间的平滑过渡。
				newcap += (newcap + 3*threshold) / 4
			}
			// 当
			// 新上限计算溢出时，将新上限设置为请求的上限。
			if newcap <= 0 {
				newcap = cap
			}
		}
	}

	var overflow bool
	var lenmem, newlenmem, capmem uintptr
	// 专门用于et.size的通用值。
	// 对于1，我们不需要任何除法/乘法。
	// goarch。PtrSize，编译器会将除法/乘法优化为常数移位。
	// 对于2的幂，使用变量移位。
	switch {
	case et.size == 1:
		lenmem = uintptr(old.len)
		newlenmem = uintptr(cap)
		capmem = roundupsize(uintptr(newcap))
		overflow = uintptr(newcap) > maxAlloc
		newcap = int(capmem)
	case et.size == goarch.PtrSize:
		lenmem = uintptr(old.len) * goarch.PtrSize
		newlenmem = uintptr(cap) * goarch.PtrSize
		capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)
		overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
		newcap = int(capmem / goarch.PtrSize)
	case isPowerOfTwo(et.size):
		var shift uintptr
		if goarch.PtrSize == 8 {
			// 为了更好地生成代码而进行掩码转换。
			shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
		} else {
			shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
		}
		lenmem = uintptr(old.len) << shift
		newlenmem = uintptr(cap) << shift
		capmem = roundupsize(uintptr(newcap) << shift)
		overflow = uintptr(newcap) > (maxAlloc >> shift)
		newcap = int(capmem >> shift)
	default:
		lenmem = uintptr(old.len) * et.size
		newlenmem = uintptr(cap) * et.size
		capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
		capmem = roundupsize(capmem)
		newcap = int(capmem / et.size)
	}

	// 除了capmem>maxAlloc之外，还需要检查溢出
	// 以防止溢出，该溢出可用于触发32位体系结构上的分段故障
	// 使用以下示例程序：
	// 
	// 键入T[1<<27+1]int64 
	// 
	// 
	// func main（）{
	// s=append（s，d，d，d，d）
	// print（len（s），“\n”）
	// }
	if overflow || capmem > maxAlloc {
		panic(errorString("growslice: cap out of range"))
	}

	var p unsafe.Pointer
	if et.ptrdata == 0 {
		p = mallocgc(capmem, nil, false)
		// /调用GrowtSlice的append（）将从旧版本覆盖。len到cap（这将是新的长度）。
		// 只清除不会被覆盖的部分。
		memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
	} else {
		// 注意：不能使用rawmem（这可以避免内存归零），因为这样GC就可以扫描未初始化的内存。
		p = mallocgc(capmem, et, true)
		if lenmem > 0 && writeBarrier.enabled {
			// 只在旧版中对指针进行着色。数组，因为我们知道目标片p 
			// 只包含nil指针，因为它在alloc期间被清除。
			bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem-et.size+et.ptrdata)
		}
	}
	memmove(p, old.array, lenmem)

	return slice{p, old.len, newcap}
}

func isPowerOfTwo(x uintptr) bool {
	return x&(x-1) == 0
}

// slicecopy用于将字符串或无指针元素切片复制到切片中。
func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int {
	if fromLen == 0 || toLen == 0 {
		return 0
	}

	n := fromLen
	if toLen < n {
		n = toLen
	}

	if width == 0 {
		return n
	}

	size := uintptr(n) * width
	if raceenabled {
		callerpc := getcallerpc()
		pc := abi.FuncPCABIInternal(slicecopy)
		racereadrangepc(fromPtr, size, callerpc, pc)
		racewriterangepc(toPtr, size, callerpc, pc)
	}
	if msanenabled {
		msanread(fromPtr, size)
		msanwrite(toPtr, size)
	}
	if asanenabled {
		asanread(fromPtr, size)
		asanwrite(toPtr, size)
	}

	if size == 1 { // 在这里做的普通案例价值约为2倍
		// TODO:使用新的memmove impl，这仍然值得吗？
		*(*byte)(toPtr) = *(*byte)(fromPtr) // 已知为字节指针
	} else {
		memmove(toPtr, fromPtr, size)
	}
	return n
}
